﻿//------------------------------------------------------------
// Shaco Framework v1.4.0
// Copyright © 2017-2020 shaco. All rights reserved.
// Feedback: mailto:449612236@qq.com
//------------------------------------------------------------

#if DEBUG_WINDOW
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using YooAsset;

namespace UnityGameFramework.Runtime
{
    public partial class DebuggerComponent
    {
        private sealed class AssetBundleWindow : ScrollableDebuggerWindowBase
        {
            struct DebugDependInfo
            {
                public string assetPath;
                public string assetShortPath;
                public int refCount;
                public string status;
            }

            struct DebugProviderInfo
            {
                public string packageName;
                public string assetPath;
                public string assetName;
                public string spawnScene;
                public string spawnTime;
                public long loadingTime;
                public int refCount;
                public string status;

                public List<DebugDependInfo> dependBundles;

                public void UpdateData(string packageName, object DebugProviderInfo)
                {
                    this.packageName = packageName;
                    // packageName = GetPropertyValue<string>(DebugProviderInfo, "PackageName");
                    assetPath = GetFiledValue<string>(DebugProviderInfo, "AssetPath");
                    assetName = System.IO.Path.GetFileName(assetPath);
                    spawnScene = GetFiledValue<string>(DebugProviderInfo, "SpawnScene");
                    spawnTime = GetFiledValue<string>(DebugProviderInfo, "SpawnTime");
                    loadingTime = GetFiledValue<long>(DebugProviderInfo, "LoadingTime");
                    refCount = GetFiledValue<int>(DebugProviderInfo, "RefCount");
                    status = GetFiledValue<string>(DebugProviderInfo, "Status");
                }
            }

            override public bool useTouchScrollView { get { return false; } }

            private string _searcName = string.Empty;
            private string _searcNameLower = string.Empty;
            private int _maxShowCount = 100;
            private string _maxShowCountStr = "200";
            private shaco.Instance.OnGUI.TouchScrollView _touchScrollView = new shaco.Instance.OnGUI.TouchScrollView();
            private shaco.Instance.OnGUI.TouchScrollView _touchScrollView2 = new shaco.Instance.OnGUI.TouchScrollView();
            private Dictionary<string, List<DebugProviderInfo>> _debugProviderData = new();
            private int _totalProviderCount = 0;
            private int _totalBundleCount = 0;
            private bool _isReverseSort = false;
            private int _prevSortIndex = -1;
            private DebugProviderInfo _currentSelectProviderInfo;
            private bool _isCurrentSelectProviderShowShortAssetPath = false;

            public override void OnEnter()
            {
                _currentSelectProviderInfo.dependBundles = null;
                _debugProviderData.Clear();
                _totalProviderCount = 0;
                _totalBundleCount = 0;
                _prevSortIndex = -1;
                _isCurrentSelectProviderShowShortAssetPath = false;
                _isReverseSort = false;

                var typeTmp = typeof(YooAssets);
                var method = typeTmp.GetMethod("GetDebugReport", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
                var debugReport = method.Invoke(null, null);
                var checkDuplicateBundleName = new Dictionary<string, bool>();
                if (debugReport != null)
                {
                    var packages = GetFiledValue<object>(debugReport, "PackageDatas");
                    TraverseList(packages, DebugPackageData =>
                    {
                        var packageName = GetFiledValue<string>(DebugPackageData, "PackageName");
                        if (!_debugProviderData.TryGetValue(packageName, out var packageData))
                        {
                            packageData = new();
                            _debugProviderData.Add(packageName, packageData);
                        }

                        var providerInfos = GetFiledValue<IList>(DebugPackageData, "ProviderInfos");
                        _totalProviderCount += providerInfos.Count;

                        TraverseList(providerInfos, DebugProviderInfo =>
                        {
                            var newDebugData = new DebugProviderInfo();
                            newDebugData.dependBundles = new();
                            newDebugData.UpdateData(packageName, DebugProviderInfo);
                            packageData.Add(newDebugData);

                            var dependBundleInfos = GetFiledValue<IList>(DebugProviderInfo, "DependBundleInfos");
                            TraverseList(dependBundleInfos, DebugBundleInfo =>
                            {
                                var bundleName = GetFiledValue<string>(DebugBundleInfo, "BundleName");
                                if (!checkDuplicateBundleName.ContainsKey(bundleName))
                                {
                                    checkDuplicateBundleName.Add(bundleName, true);
                                    ++_totalBundleCount;

                                    var newDependBundle = new DebugDependInfo();
                                    newDependBundle.assetPath = bundleName;
                                    newDependBundle.assetShortPath = shaco.Base.FileHelper.GetLastFileNameLevel(bundleName, '_', false, 3);
                                    newDependBundle.refCount = GetFiledValue<int>(DebugBundleInfo, "RefCount");
                                    newDependBundle.status = GetFiledValue<object>(DebugBundleInfo, "Status").ToString();
                                    newDebugData.dependBundles.Add(newDependBundle);
                                }
                            });
                            newDebugData.dependBundles.Sort((v2, v1) => v1.refCount.CompareTo(v2.refCount));
                        });
                    });
                }
            }

            static T GetFiledValue<T>(object target, string name, System.Reflection.BindingFlags flags = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)
            {
                var filed = target.GetType().GetField(name, flags);
                var retValue = filed.GetValue(target);
                return (T)retValue;
            }

            static T GetPropertyValue<T>(object target, string name, System.Reflection.BindingFlags flags = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)
            {
                var property = target.GetType().GetProperty(name, flags);
                var retValue = property.GetValue(target);
                return (T)retValue;
            }

            static void TraverseList(object list, System.Action<object> callback)
            {
                var listType = list.GetType(); // 获取List的类型
                if (listType.IsGenericType && listType.GetGenericTypeDefinition() == typeof(List<>)) // 检查是否为泛型List
                {
                    // var elementType = listType.GetGenericArguments()[0]; // 获取List的元素类型
                    var array = (IList)list;
                    foreach (object item in (System.Collections.IEnumerable)list) // 遍历List的元素
                    {
                        callback(item);
                    }
                }
                else
                {
                    Debug.LogError("The object is not a generic List, list=" + list);
                }
            }

            public override void OnLeave()
            {
                _searcName = string.Empty;
                _searcNameLower = string.Empty;
            }

            protected override void OnDrawScrollableWindow(Rect lastGUIRect)
            {
                float widthGUITmp = DebuggerComponent.DefaultWindowRect.width / 16;

                GUILayout.BeginVertical("box");
                {
                    GUILayout.BeginHorizontal();
                    {
                        GUILayout.Label($"Pack:{_totalProviderCount} Bundle:{_totalBundleCount} CurrentPackage:{shaco.GameHelper.res.currentPackageName}");
                        GUILayout.FlexibleSpace();
                        if (GUILayout.Button("Refresh"))
                            this.OnEnter();
                        GUILayout.Space(20f);
                        if (GUILayout.Button("Export"))
                        {
                            var typeTmp = typeof(YooAssets);
                            var method = typeTmp.GetMethod("GetDebugReport", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
                            var debugReport = method.Invoke(null, null);
                            var fileContent = JsonUtility.ToJson(debugReport, true);
                            var savePath = System.IO.Path.Combine(Application.persistentDataPath, $"DebugReport_{System.DateTime.Now.ToString("yyyy-MM-dd_HH_mm_ss")}.json");
                            Debug.Log($"export assetbundle debug report path=" + savePath);
                            System.IO.File.WriteAllText(savePath, fileContent);
#if UNITY_EDITOR
                            if (UnityEditor.EditorUtility.DisplayDialog("提醒", "是否打开日志目录", "打开", "取消"))
                                EditorUtility.RevealInFinder(savePath);
#endif
                        }
                    }
                    GUILayout.EndHorizontal();
                }
                GUILayout.EndVertical();

                GUILayout.BeginHorizontal("box");
                {
                    GUILayout.FlexibleSpace();

                    GUILayout.Label("Search Name: ");
                    GUI.changed = false;
                    _searcName = GUILayout.TextField(_searcName, GUILayout.Width(widthGUITmp * 2), GUILayout.Height(DebuggerComponent.DefaultWindowRect.height * 0.03f));
                    if (GUI.changed)
                    {
                        _searcNameLower = _searcName.ToLower();
                    }

                    GUILayout.Label("Max Show Count: ");
                    GUI.changed = false;
                    _maxShowCountStr = GUILayout.TextField(_maxShowCountStr, GUILayout.Width(widthGUITmp), GUILayout.Height(DebuggerComponent.DefaultWindowRect.height * 0.03f));
                    if (GUI.changed)
                    {
                        int tmpCount = 0;
                        if (int.TryParse(_maxShowCountStr, out tmpCount))
                            _maxShowCount = tmpCount;
                        else
                            _maxShowCountStr = _maxShowCount.ToString();
                    }
                }
                GUILayout.EndHorizontal();

                DrawAllAssetBundle();
                DrawCurrentSelectProviderInfo();
            }

            void DrawAllAssetBundle()
            {
                var perItemWidth = DebuggerComponent.DefaultWindowRect.width * 0.15f;
                int sortIndex = -1;
                GUILayout.BeginVertical("box");
                {
                    GUILayout.BeginHorizontal();
                    {
                        if (GUILayout.Button("PackageName", GUILayout.Width(perItemWidth * 0.9f))) sortIndex = 0;
                        if (GUILayout.Button("AssetbundleName", GUILayout.Width(perItemWidth * 2.2f))) sortIndex = 1;
                        // if (GUILayout.Button("SpawnScene", GUILayout.Width(perItemWidth * 0.7f))) sortIndex = 2;
                        if (GUILayout.Button("SpawnTime", GUILayout.Width(perItemWidth * 0.7f))) sortIndex = 2;
                        if (GUILayout.Button("LoadingTime(ms)", GUILayout.Width(perItemWidth * 0.85f))) sortIndex = 3;
                        if (GUILayout.Button("RefCount", GUILayout.Width(perItemWidth * 0.5f))) sortIndex = 4;
                        if (GUILayout.Button("DependCount", GUILayout.Width(perItemWidth * 0.7f))) sortIndex = 5;
                        if (GUILayout.Button("Status")) sortIndex = 6;
                    }
                    GUILayout.EndHorizontal();

                    _touchScrollView.BeginScrollView();
                    {
                        int showCountTmp = 0;
                        foreach (var iter in _debugProviderData)
                        {
                            foreach (var iter2 in iter.Value)
                            {
                                if (!string.IsNullOrEmpty(_searcNameLower))
                                {
                                    if (!iter2.assetName.ToLower().Contains(_searcNameLower))
                                        continue;
                                }

                                if (++showCountTmp > _maxShowCount)
                                    break;

                                GUILayout.BeginHorizontal();
                                {
                                    GUILayout.Label(iter2.packageName, GUILayout.Width(perItemWidth * 0.9f));
                                    if (GUILayout.Button(iter2.assetName, GUILayout.Width(perItemWidth * 2f)))
                                    {
                                        _currentSelectProviderInfo = iter2;
#if UNITY_EDITOR
                                        EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath<Object>(iter2.assetPath));
#endif
                                    }
                                    GUILayout.Space(perItemWidth * 0.2f);
                                    // GUILayout.Label(iter2.spawnScene, GUILayout.Width(perItemWidth * 0.7f));
                                    GUILayout.Label(iter2.spawnTime, GUILayout.Width(perItemWidth * 0.7f));
                                    GUILayout.Label(iter2.loadingTime.ToString(), GUILayout.Width(perItemWidth * 0.85f));
                                    GUILayout.Label(iter2.refCount.ToString(), GUILayout.Width(perItemWidth * 0.5f));
                                    GUILayout.Label(iter2.dependBundles.Count.ToString(), GUILayout.Width(perItemWidth * 0.7f));
                                    GUILayout.Label(iter2.status);
                                }
                                GUILayout.EndHorizontal();
                            }
                        }
                    }
                    _touchScrollView.EndScrollView();
                }
                GUILayout.EndVertical();

                if (sortIndex != -1)
                {
                    if (_prevSortIndex != sortIndex)
                    {
                        _prevSortIndex = sortIndex;
                        _isReverseSort = false;
                    }
                    foreach (var iter in _debugProviderData)
                    {
                        if (_isReverseSort)
                        {
                            switch (sortIndex)
                            {
                                case 0: iter.Value.Sort((v1, v2) => v1.packageName.CompareTo(v2.packageName)); break;
                                case 1: iter.Value.Sort((v1, v2) => v1.assetName.CompareTo(v2.assetName)); break;
                                // case 2: iter.Value.Sort((v1, v2) => v1.spawnScene.CompareTo(v2.spawnScene)); break;
                                case 2: iter.Value.Sort((v1, v2) => v1.spawnTime.CompareTo(v2.spawnTime)); break;
                                case 3: iter.Value.Sort((v1, v2) => v1.loadingTime.CompareTo(v2.loadingTime)); break;
                                case 4: iter.Value.Sort((v1, v2) => v1.refCount.CompareTo(v2.refCount)); break;
                                case 5: iter.Value.Sort((v1, v2) => v1.dependBundles.Count.CompareTo(v2.dependBundles.Count)); break;
                                case 6: iter.Value.Sort((v1, v2) => v1.status.CompareTo(v2.status)); break;
                                default: Debug.LogError("unsupport sort index=" + iter); break;
                            }
                        }
                        else
                        {
                            switch (sortIndex)
                            {
                                case 0: iter.Value.Sort((v2, v1) => v1.packageName.CompareTo(v2.packageName)); break;
                                case 1: iter.Value.Sort((v2, v1) => v1.assetName.CompareTo(v2.assetName)); break;
                                // case 2: iter.Value.Sort((v2, v1) => v1.spawnScene.CompareTo(v2.spawnScene)); break;
                                case 2: iter.Value.Sort((v2, v1) => v1.spawnTime.CompareTo(v2.spawnTime)); break;
                                case 3: iter.Value.Sort((v2, v1) => v1.loadingTime.CompareTo(v2.loadingTime)); break;
                                case 4: iter.Value.Sort((v2, v1) => v1.refCount.CompareTo(v2.refCount)); break;
                                case 5: iter.Value.Sort((v2, v1) => v1.dependBundles.Count.CompareTo(v2.dependBundles.Count)); break;
                                case 6: iter.Value.Sort((v2, v1) => v1.status.CompareTo(v2.status)); break;
                                default: Debug.LogError("unsupport sort index=" + iter); break;
                            }
                        }
                    }
                    _isReverseSort = !_isReverseSort;
                }
            }

            void DrawCurrentSelectProviderInfo()
            {
                if (_currentSelectProviderInfo.dependBundles.IsNullOrEmpty())
                    return;

                GUILayout.BeginVertical("box", GUILayout.Height(DebuggerComponent.DefaultWindowRect.height * 0.3f));
                {
                    _touchScrollView2.BeginScrollView();
                    {
                        var perItemWidth = DebuggerComponent.DefaultWindowRect.width * 0.15f;
                        GUILayout.BeginHorizontal();
                        {
                            if (GUILayout.Button("DependBundles"))
                                _isCurrentSelectProviderShowShortAssetPath = !_isCurrentSelectProviderShowShortAssetPath;
                            GUILayout.FlexibleSpace();
                            GUILayout.Label("RefCount", GUILayout.Width(perItemWidth * 0.5f));
                            GUILayout.Label("Status", GUILayout.Width(perItemWidth * 0.5f));
                        }
                        GUILayout.EndHorizontal();

                        foreach (var iter in _currentSelectProviderInfo.dependBundles)
                        {
                            GUILayout.BeginHorizontal();
                            {
                                GUILayout.Label(_isCurrentSelectProviderShowShortAssetPath ? iter.assetShortPath : iter.assetPath);
                                GUILayout.FlexibleSpace();
                                GUILayout.Label(iter.refCount.ToString(), GUILayout.Width(perItemWidth * 0.5f));
                                GUILayout.Label(iter.status, GUILayout.Width(perItemWidth * 0.5f));
                            }
                            GUILayout.EndHorizontal();
                        }
                    }
                    _touchScrollView2.EndScrollView();
                }
                GUILayout.EndVertical();
            }
        }
    }
}
#endif
